﻿using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Diagnostics;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using JsonPrettyPrinterPlus;
using sqCore;
using lo = sqCore.sqLogger;
using sqHlp = sqCore.sqHelpper;
using SqlAServer.Properties;


namespace SqlAServer
{
    /* Docs:
     * Summary
         * Main Window of SqlA Server. 
         * Allows the server's admin a quick and easy way to control the server (Server Up/Down, Server's DBs, Server's Tasks ...)
     * Variables
         * SqlAThread - Thread for the server.
         * HttpServerThread - Thread for the HTTP server.
         * HttpSrv - HTTP server. Communicates with SqlAClient (Not implemented yet).
         * SqlADB - The internal DB of SqlA server (MongoDB).
         * SqlADBProcess - Process for MongoServer (mongod.exe).
         * TaskList - Contains list of the current tasks of the server.
         * SelectionObject, SelectedIndex, SelectionRow - GUI current selection properties.         
     */
    public partial class MainWindow : Window
    {
     
        Thread SqlAThread;        
        bool isSqlAOn;

        sqDB SqlADB; 
        MongoDatabase SqlADBConnection;
        Process SqlADBProcess; 
        
        List<sqTask> TaskList;
        
        object SelectionObject;
        public int SelectedIndex;
        DataGridRow SelectionRow;

        /* Docs:
         * Summary: Initialize MainWindow.      
         */
        public MainWindow()
        {                  
            InitializeComponent();
            InitializeParams();
            MinimizeToTray.Enable(this); //Set the 'Close' and 'Minimize' modes of the mainWindow to 'MinizeToTray'.
            this.Closed += MainWindow_Closed; //Cleaning when closed.
            lo.DebugMode = Settings.Default.DebugMode;
            sqTask.FilesDirectory = Environment.ExpandEnvironmentVariables(Settings.Default.RepositoryPath);
        }

        /* Docs:
        * Summary: CleanAfterClosed()
        */
        void MainWindow_Closed(object sender, EventArgs e)
        {
            if (this.isSqlAOn == true)
                this.SqlAsStop();            
        }

        /* Docs:
         * Summary: Initialize Params         
         */
        private void InitializeParams()
        {
            isSqlAOn = false;
            SqlADB = new sqDB("SqlADB", "localhost", "SqlA", "Mongo", "27017", Settings.Default.SqlADBUser, Settings.Default.SqlADBPassword);
            TaskList = new List<sqTask>();
            SelectionObject = new object();
            SelectionRow = default(DataGridRow);
        }

        /* Docs:
         * Summary: Changes the 'indeterminate' mode of the working bar in GUI.
         * Variables
         */
        private void ChangeWorkingBar(bool ToWork)
        {
            if (ToWork)
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.WorkingBar.IsIndeterminate = true;
                }
                ));
            }
            else
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    this.WorkingBar.IsIndeterminate = false;
                }
                ));
            }
        }

        /* Docs:
         * Summary
             * Start or stop SqlAServer.
             * Handel sliderChange event from GUI.
         * Variables: State[double] - Current value of the GUI slider.
         */
        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (State.Value > 0)
            {
                ChangeWorkingBar(true);
                SqlAThread = new Thread(SqlAStart); SqlAThread.Start();
            }
            else
            {
                SqlAsStop();
            }
        }

        /* Docs:
        * Summary: Start SqlAServer.
            * Open Mongod.exe in new process on the local machine.
            * Load the TaskList and DBList from SqlA DB, and set them into the Gui DataGrids.            
            * Start a loop that remainds all time that SqlA is On. It search and run each sqTask, that shuold be run, on the correct time         
        * Variables: 
            * HttpSrv[MyHttpServer] - Httpserver. using SimpleHttpServer project reference.
            * StateLable[Lable] - Lable of server State.
        */
        private void SqlAStart()
        {
            lo.writeLog("SqlAServer starting up.", debugOnly: true);
            var RunMongoDB = new Task(MongoDBServerlisten); RunMongoDB.Start(); RunMongoDB.Wait();            
            var LoadLists = new Task(RefreshDataFromSlqADB); LoadLists.Start(); LoadLists.Wait();
            this.Dispatcher.Invoke((Action)(() =>
            {
                StateLable.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("LightGreen")); //Change the color of "State" lable to green, to present that SqlA is ON.
            }
            ));
            isSqlAOn = true;
            ChangeWorkingBar(false);
            this.SqlARun(); //This method should be run at SqlAThread ONLY.                             
        }

        /* Docs:
        * Summary: Stop SqlAServer.
            * Stop HttpServer.
            * Stop MongoDB Process.
            * Stop SqlAServer and abort SqlAThread.
            * Clear data from GUI.            
        * Variables: 
            * HttpSrv[MyHttpServer] - Httpserver. Using SimpleHttpServer project reference.
            * StateLable[Lable] - Lable of server State.
        */
        private void SqlAsStop()
        {
            lo.writeLog("SqlAServer shoutdown started.", debugOnly: true);
            if (!SqlADBProcess.HasExited)
                SqlADBProcess.Kill();
            if (SqlAThread.IsAlive)
                SqlAThread.Abort();
            if (SqlADBConnection.Server.State == MongoServerState.Connected)
                SqlADBConnection.Server.Disconnect();
            StateLable.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("Red")); //Change the color of "State" lable to red, to present that SqlA is OFF.
            DBTable.ItemsSource = null; ;
            TaskListTable.Items.Clear();
            ClearDBBoxs();
            isSqlAOn = false;
            lo.writeLog("SqlAServer is off.", debugOnly: true);
        }

        /* Docs:
        * Summary: Start MongoDB Server on the local machine.
            * Using Mongod.exe
        */
        private void MongoDBServerlisten()
        {
            var SqlADBPath = Environment.ExpandEnvironmentVariables(Settings.Default.SqlADBPath);
            try
            {
                File.Delete(SqlADBPath + @"\DB\mongod.lock");
                lo.writeLog("mongod.lock file has been removed.", isError: true, debugOnly: true);
            }
            catch (Exception ex)
            {
                lo.writeLog("mongod.lock file is not exists.", debugOnly: true, Param:ex.ToJson());
            }
            var SqlADBProcessInfo = new ProcessStartInfo('"' + SqlADBPath + "\\Mongo\\bin\\mongod.exe\"", "--dbpath \"" + SqlADBPath + "\\DB\""); 
            SqlADBProcessInfo.WindowStyle = ProcessWindowStyle.Hidden;
            SqlADBProcess = Process.Start(SqlADBProcessInfo);
            
        }

        /* Docs:
         */
        private void RefreshDataFromSlqADB()
        {
            this.SqlADBConnection = this.SqlADB.makeMongoConnection();
            var loadDBList = new Task(this.loadDBList); loadDBList.Start();
            var loadTaskList = new Task(this.loadTaskList); loadTaskList.Start();
            loadDBList.Wait();
            loadTaskList.Wait();
            SqlADBConnection.Server.Disconnect(); //TODO: disconnect only if time > somthing
        }

        /* Docs:
        * Summary: Loads tasks from SqlADB to GUI.
        * Variables: 
            * TaskList - List of all the tasks[sqTask].
            * TaskListTable - DataGrid for the tasks.
        * Exceptions:
            * ex1 - Thrown when the GUI terminated, but SqlA server thread is still running.
            * ex2- Thrown when the system cannot get the sqTasks from the SqlADB, can happand if MongoDBServer is down.
        */
        private void loadTaskList()
        {
            var getTasks = new sqMongoQueryJob<sqTask>(SqlADB, CollectionName: "TaskList");
            try
            {
                getTasks.executeJob(sortBy: new string[] { "IsActive", "NextRunTime", "Name" }, SortType: sqHlp.SortType.Ascending);
                this.TaskList = ((sqQueryTreeResult<sqTask>)getTasks.getResult()).Params;
                try
                {
                    this.Dispatcher.Invoke((Action)(() =>
                    {
                        TaskListTable.Items.Clear();
                        foreach (sqTask task in TaskList)
                        {
                            TaskListTable.Items.Add(task);
                        }                        
                    }
                    ));
                }
                catch
                {
                    lo.writeLog(LogMessage: "The Gui terminated, Cannot display TaskList", isError: true);
                }
            }
            catch (Exception ex2)
            {
                lo.writeLog(LogMessage: "The system failed to exeute the job.", isError: true, Params: new List<string> { ex2.ToString(), getTasks.ToJson() });
            }
        }

        /* Docs:
        * Summary: Load DBs from SqlADB to GUI.
        * Variables: 
            * DBList - List of all the DBs[sqDB].
            * DBListTable - DataGrid of the DBs.
        * Exceptions:
            * ex1 - Thrown when the GUI terminated, but SqlA server thread is still running.
            * ex2- Thrown when the system cannot get the sqDBs from the SqlADB, can happand if MongoDBServer is down.
        */
        private void loadDBList()
        {
            var getDBs = new sqMongoQueryJob<sqDB>(SqlADB, CollectionName: "DBList");
            try
            {
                getDBs.executeJob();
                var DBList = ((sqQueryTreeResult<sqDB>)getDBs.getResult()).Params;
                try
                {
                    this.Dispatcher.Invoke((Action)(() =>
                    {
                        DBTable.ItemsSource = CollectionViewSource.GetDefaultView(DBList);
                        /*DBTable.Items.Clear();
                        foreach (sqDB db in DBList)
                        {
                            DBTable.Items.Add(db);
                        }*/
                    }
                    ));
                }
                catch
                {
                    lo.writeLog(LogMessage: "The Gui terminated, Cannot display DBList", isError: true);
                }
            }
            catch (Exception ex)
            {
                lo.writeLog(LogMessage: "The system failed to exeute the job.", isError: true, Params: new List<string> { ex.ToString(), getDBs.ToJson() });
            }
        }

        /* Docs:
        * Summary: Search and run each sqtask that shuold be run, on the currect time
            * Check each sqTask NextTimeRun and Run it, if it's her time.
            * Update SqlADB.
            * Sleep until next task time.
            * Update GUI TaskList     
        */
        private void SqlARun()
        {
            lo.writeLog("SqlAServer is Running.", debugOnly: true);
            DateTime Now;
            while (isSqlAOn)
            {                
                Now = DateTime.Now;
                lo.writeLog("SqlAServer Starting Tasks round.", Param: Now.ToString(), debugOnly: true);                
                var tasks = TaskList
                    .Where(x => x.NextRunTime.ToUniversalTime()<= Now.ToUniversalTime() && x.IsActive)
                    .Select(x => Task.Factory.StartNew(() => x.runTask()).ContinueWith(t => ChangeTask(x, x), TaskContinuationOptions.NotOnFaulted))
                    .ToArray();
                Task.WaitAll(tasks);
                lo.writeLog("All tasks finish.", debugOnly: true);
                System.Threading.Thread.Sleep(this.GetNextTaskTime());
                lo.writeLog("SqlAServer try to refresh the TaskList.", debugOnly: true);
                var RefreshTasks = new Task(loadTaskList); RefreshTasks.Start(); RefreshTasks.Wait();
            }
        }

        /* Docs:
         * Summary: Find the next task and returns the remaining time in milliseconds.
            * Ensures that minimum time is 10 seconds and maximum time is 2 hours.
         * Exceptions:
            * ex - Thrown when the system failed to calculate the next time. can happend if SqlADB is down or if there is no tasks in the TaskList.
         */
        private int GetNextTaskTime()
        {
            try
            {
                var nextTask = new sqMongoQueryJob<sqTask>(SqlADB, CollectionName: "TaskList", QueryDocs: new QueryDocument() {{ "IsOk", true }});
                nextTask.executeJob(limit: 1, sortBy: new string[] { "NextRunTime" }, SortType: sqHlp.SortType.Ascending); //get the next task.
                var nextTime = nextTask.QueryResult.Params[0].getNextRunTime();
                var waitTime = (int)((nextTime.ToUniversalTime()).Subtract(DateTime.Now.ToUniversalTime()).TotalMilliseconds);
                lo.writeLog(DateTime.Now.ToUniversalTime().ToString()+"||"+nextTime.ToUniversalTime().ToString() + "||" + ((DateTime.Now.ToUniversalTime() - nextTime.ToUniversalTime()).TotalMilliseconds).ToString(), debugOnly:true);
                if (waitTime > 7200000) //max 2h
                    waitTime = 7200000;
                if (waitTime < 120000) //min 2mi
                    waitTime = 120000;
                lo.writeLog("Next round in:" + waitTime.ToString(), debugOnly: true, Param: waitTime.ToString());
                return waitTime;
            }
            catch (System.Exception ex)
            {
                lo.writeLog("Failed calculating time remaining to the next round. Decided to wait 2 hours.", isError: true, Param: ex.ToString());
                return 720000; //set to max
            }

        }

        /* Docs:
         * Summary: Change Selection object and row. Handel item DoubleClick event from GUI TaskList DataGrid.         
         */
        private void TaskListTable_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            try
            {
                var row = this.TaskListTable.ItemContainerGenerator.ContainerFromItem(TaskListTable.CurrentItem) as DataGridRow;
                this.ChangeSelectionRow(row);
                this.SelectionObject = (sqTask)(((DataGrid)sender).CurrentItem);
                this.ClearDBBoxs();
            }
            finally { }
        }

        /* Docs:
         * Summary: Clear all GUI DB's Params boxs.
         */
        private void ClearDBBoxs()
        {
            NameBox.Text = "";
            HostBox.Text = "";
            PortBox.Text = "";
            ServiceNameBox.Text = "";
            PassBox.Text = "";
            UserNameBox.Text = "";
            TypeCBox.Text = "";
        }

        /* Docs:
         * Summary: Change Selection object and row. Handel item DoubleClick event from GUI DBlist DataGrid.         
         */
        private void DBTable_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            try
            {
                var row = this.DBTable.ItemContainerGenerator.ContainerFromItem(DBTable.CurrentItem) as DataGridRow;
                this.ChangeSelectionRow(row);
                this.SelectionObject = (sqDB)(((DataGrid)sender).CurrentItem);
                this.setDBBoxs((sqDB)this.SelectionObject);
            }
            finally { }
        }

        /* Docs:
         * Summary: Set DB params to GUI boxs.
         */
        private void setDBBoxs(sqDB DB)
        {
            NameBox.Text = (DB).Name;
            HostBox.Text = (DB).Host;
            PortBox.Text = (DB).Port;
            ServiceNameBox.Text = (DB).ServiceName;
            PassBox.Text = (DB).Password;
            UserNameBox.Text = (DB).UserName;
            TypeCBox.Text = (DB).DBType.ToString();
        }

        /* Docs:
        * Summary: Change selection row and row highlist.        
        * Variables: 
            * row - New selection row.
        */
        private void ChangeSelectionRow(DataGridRow row = default(DataGridRow))
        {
            if (row == default(DataGridRow))
                row = SelectionRow;
            if (this.SelectionRow != default(DataGridRow))
                this.SelectionRow.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFAAA6A6"));
            row.Background = Brushes.LightGoldenrodYellow;
            SelectionRow = row;
        }

        /* Docs:
        * Summary: Update the selected DB, with the DB's params boxs data.
            * Handel PushButtenClicked event.
        * Variables: 
            * dbtemp - DB with params from GUI DB's params boxs.
        */
        private void ChangeDBClicked(object sender, RoutedEventArgs e)
        {
            if (this.SelectionObject.GetType() == typeof(sqDB) && isSqlAOn)
            {
                sqDB dbtemp = new sqDB(NameBox.Text, HostBox.Text, ServiceNameBox.Text, TypeCBox.Text, PortBox.Text, UserNameBox.Text, PassBox.Text);
                dbtemp.Id = ((sqDB)this.SelectionObject).Id;
                Task ChangeDB = new Task(() => { this.ChangeDB((sqDB)this.SelectionObject, dbtemp); }); ChangeDB.Start();
            }
        }

        /* Docs:
        * Summary: Updates DB in the SqlADB.
            * Check if the new DB params is valid before the upadate.
        * Variables: 
            * OldDB - The DB we want to update
            * NewDB - DB with the new params.
        * Exceptions:            
            * ex- Thrown when the system cannot change the sqDBs in SqlADB, can happand if MongoDBServer is down.
        */
        private void ChangeDB(sqDB OldDB, sqDB NewDB)
        {
            ChangeWorkingBar(true);
            if (!NewDB.checkConnection()) //check if the new DB params is valid.
                lo.writeLog("The system failed to connect to the DB, Check the DB params and try again.", Params: new List<string>() { NewDB.getDBParams().ToJson().ToString() }, CreateMessageBox: true, isError: true);
            else
            {
                try
                {
                    lo.writeLog("debug1", debugOnly: true);
                    sqMongoNonQueryJob updateDB = new sqMongoNonQueryJob(SqlADB, CollectionName: "DBList", QueryDocs: new QueryDocument() { { "_id", NewDB.Id } }, ObjectDocs: NewDB);
                    lo.writeLog("debug2", debugOnly: true);
                    updateDB.executeJob<sqDB>();
                    lo.writeLog("The DB has been update.", Params: new List<string>() { NewDB.Name }, debugOnly:true);
                }
                catch (System.Exception ex) { lo.writeLog("The system failed to connect to SqlA DataBase. Try again.", Params: new List<string>() { ex.ToString(), NewDB.ToJson() }, isError: true, CreateMessageBox:true); }
                Task RefreshDBList = new Task(loadDBList); RefreshDBList.Start(); RefreshDBList.Wait();
            }
            ChangeWorkingBar(false);
        }

        /* Docs:
        * Summary: Remove the selection DB.
            * Handel PushButtenClicked event.
        */
        private void RemoveDBClicked(object sender, RoutedEventArgs e)
        {
            if (this.SelectionObject.GetType() == typeof(sqDB) && isSqlAOn)
            {
                ChangeWorkingBar(true);
                Task RemoveDB = new Task(() => { this.RemoveDB((sqDB)this.SelectionObject); }); RemoveDB.Start();
            }
        }

        /* Docs:
        * Summary: Remove DB from the SqlADB.
        * Variables:             
            * DB - The DB we want to remove.
        * Exceptions:            
            * ex- Thrown when the system cannot Remove sqDB in SqlADB, can happand if MongoDBServer is down.
        */
        private void RemoveDB(sqDB DB)
        {
            try
            {
                sqMongoNonQueryJob RemoveDB = new sqMongoNonQueryJob(SqlADB, sqHlp.MongoCommand.remove, "DBList", new QueryDocument() { { "_id", DB.Id } });
                RemoveDB.executeJob<sqDB>();
                lo.writeLog("The DB has been removed.", Params: new List<string>() { DB.Name });
            }
            catch (System.Exception ex)
            {
                lo.writeLog("Failed to remove the DB.", Params: new List<string>() { ex.ToString(), DB.Name }, CreateMessageBox: true, isError: true);
            }
            Task RefreshDBList = new Task(loadDBList); RefreshDBList.Start(); RefreshDBList.Wait();
            ChangeWorkingBar(false);
        }

        /* Docs:
        * Summary: Create New selection DB, with the DB's params boxs data.
            * Handel PushButtenClicked event.
        * Variables: 
            * dbtemp - DB with params from GUI DB's params boxs.
        */
        private void AddNewDBClicked(object sender, RoutedEventArgs e)
        {
            if (isSqlAOn)
            {
                ChangeWorkingBar(true);
                sqDB dbtemp = new sqDB(NameBox.Text, HostBox.Text, ServiceNameBox.Text,TypeCBox.Text, PortBox.Text, UserNameBox.Text, PassBox.Text);
                Task AddNewDB = new Task(() => { this.AddNewDB(dbtemp); }); AddNewDB.Start();
            }
        }

        /* Docs:
        * Summary: Add new DB in the SqlADB.
            * Check if the new DB params is valid before the upadate.
        * Variables:             
            * DB - DB with the new params.
        * Exceptions:            
            * ex- Thrown when the system cannot Add sqDB in SqlADB, can happand if MongoDBServer is down.
        */
        private void AddNewDB(sqDB DB)
        {
            if (!DB.checkConnection())
                lo.writeLog("The system failed to connect to the DB, Check the DB params and try again.", Params: new List<string>() { DB.getDBParams().ToJson().ToString() }, CreateMessageBox: true, isError: true);
            else
            {
                try
                {
                    sqMongoNonQueryJob InsertDB = new sqMongoNonQueryJob(SqlADB, sqHlp.MongoCommand.insert, CollectionName: "DBList", ObjectDocs: DB);
                    InsertDB.executeJob<sqDB>();
                    lo.writeLog("The DB has been added.", Params: new List<string>() { DB.Name });
                }
                catch (System.Exception ex)
                {
                    lo.writeLog("The system failed to connect to SqlA DataBase. Try again.", Params: new List<string>() { ex.ToString() }, CreateMessageBox: true, isError: true);
                }
                Task RefreshDBList = new Task(loadDBList); RefreshDBList.Start(); RefreshDBList.Wait();
                ChangeWorkingBar(false);
            }
        }

        /* Docs:
        * Summary: Update Task in the SqlADB.
        * Variables: 
            * OldTask - The Task we want to update
            * NewTask - Task with the new params.
            * paramChanged - Description of the changing params.
        * Exceptions:            
            * ex- Thrown when the system cannot change the sqDBs in SqlADB, can happand if MongoDBServer is down.
        */
        private void ChangeTask(sqTask OldTask, sqTask NewTask, string paramChanged = default(string))
        {
            ChangeWorkingBar(true);
            try
            {
                sqMongoNonQueryJob updateTask = new sqMongoNonQueryJob(SqlADB, CollectionName: "TaskList", QueryDocs: new QueryDocument() { { "_id", OldTask.Id } }, ObjectDocs: NewTask);
                updateTask.executeJob<sqTask>();
                lo.writeLog("The Task has been update.", Params: new List<string>() { NewTask.Name, paramChanged.ToJson() });
            }
            catch (System.Exception ex) { lo.writeLog("The system faild connect to SqlA DataBase. Try again.", Params: new List<string>() { ex.ToString(), NewTask.ToJson() }, isError: true); }
            Task RefreshTaskList = new Task(loadTaskList); RefreshTaskList.Start();
            ChangeWorkingBar(false);
        }

        /* Docs:
        * Summary: Change active state of the selection task.
            * Handel PushButtenClicked event.
        */
        private void TaskActiveChangeClicked(object sender, RoutedEventArgs e)
        {
            if (this.SelectionObject.GetType() == typeof(sqTask) && isSqlAOn)
            {
                this.ChangeTaskActiveState((sqTask)this.SelectionObject);
            }
        }

        /* Docs:
        * Summary: Change Task active state in the SqlADB.
        * Variables: 
            * Task - The Task we want to update
        */
        private void ChangeTaskActiveState(sqTask Task)
        {
            Task.changeTaskActiveState();
            ChangeTask(Task, Task, "isActive");
        }

        /* Docs:
        * Summary: Runs the selection Task.
            * Handel PushButtenClicked event.
        */
        private void RunNowClicked(object sender, RoutedEventArgs e)
        {
            if (this.SelectionObject.GetType() == typeof(sqTask) && isSqlAOn)
            {
                var task = Task.Factory.StartNew(() => this.RunTaskNow((sqTask)this.SelectionObject));
            }
        }

        /* Docs:
        * Summary: Run specific Task.
            * Runs the Task without change her timings.
        * Variables: 
            * Task - The Task we want to Run.
        */
        private void RunTaskNow(sqTask Task)
        {
            ChangeWorkingBar(true);
            try
            {
                TaskList[TaskList.IndexOf(Task)].runTask(true);
                lo.writeLog("The task Run successfully.", CreateMessageBox: true, debugOnly: true);
            }
            catch (Exception ex)
            {
                lo.writeLog("Faild to run the Task.", CreateMessageBox: true, Params: new List<string>() { ex.ToString(), Task.ToJson() }, isError: true);
            }
            ChangeWorkingBar(false);
        }

        /* Docs:
        * Summary: Set Task's next timing without running her.
            * Handel PushButtenClicked event.
        */
        private void NextClicked(object sender, RoutedEventArgs e)
        {
            if (this.SelectionObject.GetType() == typeof(sqTask) && isSqlAOn)
            {
                this.TaskNext((sqTask)this.SelectionObject);
            }
        }

        /* Docs:
        * Summary: Set Task's next timing without running her.
        * Variables: 
            * Task - The Task we want to change.
        */
        private void TaskNext(sqTask Task)
        {
            ChangeWorkingBar(true);
            Task.next(isRun: false);
            this.ChangeTask(Task, Task, "NextRunTime");
            lo.writeLog("The 'Task.Next'.", TaskName: Task.Name, debugOnly: true);
            ChangeWorkingBar(false);
        }

        /* Docs:
        * Summary: Set Task's next timing to closed next time to now, without running her.
            * Handel PushButtenClicked event.
        */
        private void NextToNowClicked(object sender, RoutedEventArgs e)
        {
            if (this.SelectionObject.GetType() == typeof(sqTask) && isSqlAOn)
            {
                this.TaskNextToNow((sqTask)this.SelectionObject);
            }

        }

        /* Docs:
        * Summary: Set Task's 'nextTime' to the closest 'nextTime' to now, without running her.
        * Variables: 
            * Task - The Task we want to change.
        */
        public void TaskNextToNow(sqTask Task)
        {
            ChangeWorkingBar(true);
            Task.nextToNow(sqHlp.NextType.after);
            this.ChangeTask(Task, Task, "nextRunTime");
            lo.writeLog("The task time set to NowTime'.", TaskName: Task.Name, debugOnly: true);
            ChangeWorkingBar(false);
        }

        /* Docs:
        * Summary: Get selection Task json.
            * Open new Dialog window.
            * Set the Task Json text code in the window.
            * Set new events to window's buttens.
        * Variables: 
            * win - Dialog window.
            * DTextBox - TextBox in win.
            * ReloadPB - PushButten in win.
            * SavePB - PushButten in win.
        * Events:
            * ReloadPB.Click -> DialogWindowReloadPBClicked() - Reload the Task Json code to DTextBox.
            * ReloadPB.SavePB -> DialogWindowSavePBClicked() - Save the edited Task.
        */
        private void TaskEditJsonClicked(object sender, RoutedEventArgs e)
        {
            if (this.SelectionObject.GetType() == typeof(sqTask) && isSqlAOn)
            {
                string BJTask = sqHlp.GetTaskJsonPreaty(((sqTask)this.SelectionObject));
                var win = new Dialog();
                win.DTextBox.Document.Blocks.Clear();
                win.DTextBox.AppendText(BJTask);
                win.text = BJTask;
                win.ReloadPB.Click += DialogWindowReloadPBClicked;
                win.SavePB.Click += DialogWindowSavePBClicked;
                win.Show();
            }
        }

        /* Docs:
        * Summary: Save the edited Task.
            * Handel PushButtenClicked event.
        * Variables: 
            * task - The edited Task from the GUI.
        */
        private void DialogWindowSavePBClicked(object sender, RoutedEventArgs e)
        {
            Dialog win = ((Dialog)(Window.GetWindow((Button)sender)));
            string TaskText = new TextRange(win.DTextBox.Document.ContentStart, win.DTextBox.Document.ContentEnd).Text.ToString();
            sqTask task = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<sqTask>(TaskText);
            ChangeTask(task, task);
            win.Close();
        }

        /* Docs:
        * Summary: Reload the Task Json code to DTextBox.
            * Handel PushButtenClicked event.
        */
        private void DialogWindowReloadPBClicked(object sender, RoutedEventArgs e)
        {
            Dialog win = ((Dialog)(Window.GetWindow((Button)sender)));
            win.Dispatcher.Invoke((Action)(() =>
            {
                win.DTextBox.Document.Blocks.Clear();
                win.DTextBox.AppendText(win.text);
            }
            ));
        }

        /* Docs:
        * Summary: Get selection Task logs.
            * Open new TableDialog window.
            * Set the logs in the window DataGrid.
        * Variables: 
            * win - TableDialog window.
            * logs - list of sqLogs of the task.
            * table - DataGrid in win.
        */
        private void TaskGetLogsClicked(object sender, RoutedEventArgs e)
        {            
            if (this.SelectionObject.GetType() == typeof(sqTask) && isSqlAOn)
            {
                ChangeWorkingBar(true);
                var logs = this.TaskGetLogs((sqTask)SelectionObject);                
                if (!(logs.Count() > 0))
                    lo.writeLog("The task has'nt logs yet.", isError: true, CreateMessageBox: true, Param: ((sqTask)SelectionObject).Name);
                else
                {
                    var win = new TableDialog();
                    var logsView = CollectionViewSource.GetDefaultView(logs);
                    win.Table.ItemsSource = logsView;
                    /*InitializeLogsTableDialog(win); //Create the DataGrid in the window.
                    foreach (sqLog L in logs)
                    {
                        win.Table.Items.Add(L);
                    }*/
                    ChangeWorkingBar(false);
                    win.Show();                    
                }
            }
        }
        
        /* Docs:
        * Summary: Get Task's logs from SqlADB.
        */
        private List<sqLog> TaskGetLogs(sqTask Task)
        {
            var GetLogs = new sqMongoQueryJob<sqLog>(SqlADB, "Logs", new QueryDocument() { { "TaskName", ((sqTask)SelectionObject).Name } });
            GetLogs.executeJob(100, sortBy: new string[] { "Time" });
            return ((sqQueryTreeResult<sqLog>)GetLogs.getResult()).Params;
        }

        /* Docs:
        * Summary: Initialize the table params in TableaDialog.
            * bind to sqLog objects
        */
        private void InitializeLogsTableDialog(TableDialog win)
        {
            DataGridTextColumn Log = new DataGridTextColumn();
            DataGridTextColumn Time = new DataGridTextColumn();
            DataGridTextColumn Params = new DataGridTextColumn();
            win.Table.Columns.Add(Time);
            win.Table.Columns.Add(Log);
            win.Table.Columns.Add(Params);
            Time.Header = "Time";
            Log.Header = "Message";
            Params.Header = "Params";
            Time.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            Log.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            Params.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            Time.Binding = new Binding("Time");      //bind to params of sqLog type.
            Log.Binding = new Binding("LogMessage");
            Params.Binding = new Binding("Params");            
        }

        /* Docs:
        * Summary: Find the last selected object in the DataGrids and restore the line highlight.
            * Handel DataGrid RowLoad event.
        */
        private void TaskListTable_LoadingRow(object sender, DataGridRowEventArgs e)
        {
            this.FindSelectionObject();
        }

        /* Docs:
        * Summary: Find the last selected object in the DataGrids and restore the line highlight.
            * Handel DataGrid RowLoad event.
        */
        private void DBTable_LoadingRow(object sender, DataGridRowEventArgs e)
        {
            this.FindSelectionObject();
        }

        /* Docs:
        * Summary: Find the last selected object in the DataGrids and restore the line highlight.
        * Variables: 
            * SelectionObject - The last object that selected by the user, from one of the GUI DataGrids.
            * SelectedIndex - The index of selection item in his DataGrid.            
            * row[DataGridRow] - The Row of the selection item.
            * SelectionRow - The last row of the last selection item.           
        */
        private void FindSelectionObject()
        {
            try
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    DataGridRow row = new DataGridRow();
                    if (SelectionObject.GetType() == typeof(sqTask))
                    {
                        this.SelectedIndex = TaskListTable.Items.IndexOf(((sqTask)SelectionObject));
                        row = TaskListTable.ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as DataGridRow;
                    }
                    else if (SelectionObject.GetType() == typeof(sqDB))
                    {
                        this.SelectedIndex = DBTable.Items.IndexOf(((sqDB)SelectionObject));
                        row = DBTable.ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as DataGridRow;
                    }

                    row.Background = Brushes.LightGoldenrodYellow;
                    this.SelectionRow = row;
                }));
            }
            catch { }
        }

    }

}
